<template>
	<view class="page">
		<!-- 搜索卡片 -->
		<view class="search-card">
			<view class="form-group">
				<text class="form-label">📊 股票代码/名称</text>
				<view class="input-wrapper">
					<input 
						class="form-input" 
						type="text"
						v-model="searchKeyword"
						@input="onSearchInput"
						placeholder="请输入股票代码或名称"
						:disabled="stockSelected"
					/>
					<!-- 清除按钮 -->
					<view v-if="searchKeyword" class="clear-btn" @click="clearSearch">
						<text class="icon">✕</text>
					</view>
					<!-- 自动完成列表 -->
					<view v-if="showAutocomplete && autocompleteList.length > 0" class="autocomplete-list">
						<view 
							v-for="(item, index) in autocompleteList" 
							:key="index"
							class="autocomplete-item"
							@click="selectStock(item)"
						>
							<text class="stock-code">{{ item.stockCode }}</text>
							<text class="stock-name">{{ item.stockName }}</text>
						</view>
					</view>
				</view>
			</view>
			
			<view class="form-group">
				<text class="form-label">📈 复权方式</text>
				<picker 
					mode="selector" 
					:range="adjustOptions" 
					:value="adjustIndex"
					@change="onAdjustChange"
				>
					<view class="form-select">
						<text>{{ adjustOptions[adjustIndex] }}</text>
						<text class="arrow">▼</text>
					</view>
				</picker>
			</view>
			
			<view class="form-group">
				<text class="form-label">📏 数据长度</text>
				<input 
					class="form-input" 
					type="number"
					v-model.number="dataLength"
					placeholder="1-1023之间"
					:min="1"
					:max="1023"
				/>
			</view>
			
			<button 
				class="btn-primary" 
				:disabled="!stockSelected || loading"
				@click="queryKLine"
			>
				<text v-if="loading">查询中...</text>
				<text v-else>🔍 查询K线</text>
			</button>
		</view>
		
		<!-- K线周期Tab -->
		<view class="tab-container">
			<view class="tabs">
				<view 
					v-for="(tab, index) in periodTabs" 
					:key="index"
					class="tab"
					:class="{ active: currentPeriod === tab.value }"
					@click="switchPeriod(tab.value)"
				>
					{{ tab.label }}
				</view>
			</view>
		</view>
		
		<!-- 时间选择器 -->
		<view v-if="hasData && timeList.length > 0" class="time-selector">
			<text class="time-label">🕐 时间选择</text>
			<view class="time-select-btn" @click="showTimePicker">
				<text class="time-text">{{ selectedTime || '请选择时间' }}</text>
				<text class="arrow">▼</text>
			</view>
		</view>
		
		<!-- K线图展示 -->
		<view v-if="hasData" class="chart-card">
			<view class="chart-header">
				<view class="stock-info">
					<view>
						<text class="stock-title">{{ stockData.stockName }} ({{ stockData.stockCode }})</text>
					</view>
					<view style="text-align: right;">
						<text class="stock-price">¥{{ stockData.lastPrice.toFixed(2) }}</text>
						<text 
							class="stock-change"
							:class="stockData.priceChange >= 0 ? 'positive' : 'negative'"
						>
							{{ stockData.priceChange >= 0 ? '▲' : '▼' }}
							{{ Math.abs(stockData.priceChange).toFixed(2) }} 
							({{ stockData.priceChangePercent.toFixed(2) }}%)
						</text>
					</view>
				</view>
			</view>
			
		<!-- K线图canvas -->
		<view class="chart-container">
			<!-- #ifndef MP-WEIXIN -->
			<canvas 
				canvas-id="klineChart" 
				id="klineChart"
				class="chart-canvas"
				@touchstart="onChartTouchStart"
				@touchmove="onChartTouchMove"
				@touchend="onChartTouchEnd"
			></canvas>
			<!-- #endif -->
			<!-- #ifdef MP-WEIXIN -->
			<canvas 
				type="2d"
				canvas-id="klineChart" 
				id="klineChart"
				class="chart-canvas"
				@touchstart="onChartTouchStart"
				@touchmove="onChartTouchMove"
				@touchend="onChartTouchEnd"
			></canvas>
			<!-- #endif -->
			
				<!-- K线详情弹窗 -->
				<view v-if="showKLineDetail" class="kline-detail-popup">
					<view class="detail-item">
						<text class="detail-label">时间:</text>
						<text class="detail-value">{{ klineDetail.time }}</text>
					</view>
					<view class="detail-item">
						<text class="detail-label">开盘:</text>
						<text class="detail-value">¥{{ klineDetail.open }}</text>
					</view>
					<view class="detail-item">
						<text class="detail-label">收盘:</text>
						<text class="detail-value">¥{{ klineDetail.close }}</text>
					</view>
					<view class="detail-item">
						<text class="detail-label">最高:</text>
						<text class="detail-value positive">¥{{ klineDetail.high }}</text>
					</view>
					<view class="detail-item">
						<text class="detail-label">最低:</text>
						<text class="detail-value negative">¥{{ klineDetail.low }}</text>
					</view>
					<view class="detail-item">
						<text class="detail-label">成交量:</text>
						<text class="detail-value">{{ formatVolume(klineDetail.volume) }}</text>
					</view>
				</view>
			</view>
			
			<!-- 数据指标 -->
			<view class="metrics-grid">
				<view class="metric-item">
					<text class="metric-label">开盘</text>
					<text class="metric-value">{{ stockData.todayPrice.toFixed(2) }}</text>
				</view>
				<view class="metric-item">
					<text class="metric-label">收盘</text>
					<text class="metric-value">{{ stockData.closePrice.toFixed(2) }}</text>
				</view>
				<view class="metric-item">
					<text class="metric-label">最高</text>
					<text class="metric-value positive">{{ stockData.highPrice.toFixed(2) }}</text>
				</view>
				<view class="metric-item">
					<text class="metric-label">最低</text>
					<text class="metric-value negative">{{ stockData.lowPrice.toFixed(2) }}</text>
				</view>
				<view class="metric-item">
					<text class="metric-label">成交量</text>
					<text class="metric-value">{{ formatVolume(stockData.volume) }}</text>
				</view>
				<view class="metric-item">
					<text class="metric-label">成交额</text>
					<text class="metric-value">{{ formatTurnover(stockData.turnover) }}</text>
				</view>
			</view>
		</view>
		
		<!-- 四度空间（市场轮廓图TPO） -->
		<view v-if="hasData" class="tpo-card">
			<view class="tpo-header">
				<text class="tpo-title">📊 四度空间</text>
				<view class="tpo-controls">
					<view class="tpo-control-item">
						<text class="tpo-control-label">分度值</text>
						<input 
							class="tpo-input" 
							type="digit"
							v-model="tpoTickSize"
							@blur="onTpoTickSizeChange"
							placeholder="0.191"
						/>
					</view>
					<view class="tpo-control-item">
						<text class="tpo-control-label">填充值</text>
						<picker 
							mode="selector" 
							:range="tpoFillOptions" 
							:value="tpoFillIndex"
							@change="onTpoFillChange"
						>
							<view class="tpo-select">
								<text>{{ tpoFillOptions[tpoFillIndex] }}</text>
								<text class="arrow">▼</text>
							</view>
						</picker>
					</view>
				</view>
			</view>
			
		<!-- TPO图canvas -->
		<view class="tpo-container">
			<!-- #ifndef MP-WEIXIN -->
			<canvas 
				canvas-id="tpoChart" 
				id="tpoChart"
				class="tpo-canvas"
				:style="{ height: tpoCanvasHeight + 'px' }"
				@touchstart="onTPOTouchStart"
				@touchmove="onTPOTouchMove"
				@touchend="onTPOTouchEnd"
			></canvas>
			<!-- #endif -->
			<!-- #ifdef MP-WEIXIN -->
			<canvas 
				type="2d"
				canvas-id="tpoChart" 
				id="tpoChart"
				class="tpo-canvas"
				:style="{ height: tpoCanvasHeight + 'px' }"
				@touchstart="onTPOTouchStart"
				@touchmove="onTPOTouchMove"
				@touchend="onTPOTouchEnd"
			></canvas>
			<!-- #endif -->
		</view>
			
			<!-- 导出按钮 -->
			<button class="btn-export" @click="exportTPOToExcel">📥 导出Excel</button>
		</view>
		
		<!-- 时间选择弹窗 -->
		<view v-if="pickerVisible" class="picker-overlay" @click="hideTimePicker">
			<view class="picker-container" @click.stop>
				<view class="picker-header">
					<button class="picker-cancel" @click="hideTimePicker">取消</button>
					<text class="picker-title">选择时间</text>
					<button class="picker-confirm" @click="confirmTime">确定</button>
				</view>
				<scroll-view class="picker-content" scroll-y>
					<view 
						v-for="(time, index) in reversedTimeList" 
						:key="index"
						class="picker-item"
						:class="{ selected: tempSelectedIndex === (fullPartList.length - 1 - index) }"
						@click="selectTime(fullPartList.length - 1 - index)"
					>
						{{ formatDetailTime(fullPartList[fullPartList.length - 1 - index][0]) }}
					</view>
				</scroll-view>
			</view>
		</view>
		
		<!-- 底部TabBar -->
		<view class="tab-bar">
			<view class="tab-bar-item active">
				<text class="tab-bar-icon">🏠</text>
				<text class="tab-bar-label">查询</text>
			</view>
			<view class="tab-bar-item" @click="goToHistory">
				<text class="tab-bar-icon">📝</text>
				<text class="tab-bar-label">历史</text>
			</view>
		</view>
	</view>
</template>

<script>
import api from '../../api/index.js';
import storage from '../../utils/storage.js';
import * as XLSX from 'xlsx';

	export default {
		data() {
			return {
			// 搜索相关
			searchKeyword: '',
			autocompleteList: [],
			showAutocomplete: false,
			stockSelected: false,
			selectedStock: null,
			searchTimer: null,
			
			// 查询参数
			adjustOptions: ['前复权', '不复权', '后复权'],
			adjustIndex: 0,
			dataLength: 36,
			
			// 周期Tab
			periodTabs: [
				{ label: '5分', value: 'm5' },
				{ label: '30分', value: 'm30' },
				{ label: '60分', value: 'm60' },
				{ label: '日K', value: 'day' },
				{ label: '周K', value: 'week' },
				{ label: '月K', value: 'month' }
			],
			currentPeriod: 'day',
			
			// 数据相关
			loading: false,
			hasData: false,
			stockData: {},
			partList: [],
			fullPartList: [],
			timeList: [],
			selectedTimeIndex: -1,
			selectedTime: '',
			
			// 时间选择器
			pickerVisible: false,
			tempSelectedIndex: -1,
			
			// 图表相关
			chartContext: null,
			canvasNode: null,
			canvasWidth: 0,
			canvasHeight: 0,
			dpr: 1,
			canvasReady: false, // Canvas 是否已初始化完成
			touchStartX: 0,
			klinePadding: { top: 20, right: 10, bottom: 30, left: 45 },
			
			// K线详情弹窗
			showKLineDetail: false,
			klineDetail: {
				time: '',
				open: 0,
				close: 0,
				high: 0,
				low: 0,
				volume: 0
			},
			detailTimer: null,
			
			// K线图十字标线
			showKLineCrossHair: false,
			klineCrossHairX: 0,
			klineCrossHairY: 0,
			
			// TPO（四度空间）相关
			tpoTickSize: 0.191, // 分度值
			tpoFillOptions: ['文字', '字母'], // 填充值选项
			tpoFillIndex: 0, // 当前选中的填充值索引
			tpoFillCharsList: {
				'字母': ['A', 'B', 'C', 'D', 'E'],
				'文字': ['潜', '见', '慎', '跃', '飞']
			},
			tpoContext: null, // TPO Canvas上下文
			tpoCanvasNode: null, // TPO Canvas节点
			tpoCanvasWidth: 0, // TPO Canvas宽度
			tpoCanvasHeight: 300, // TPO Canvas高度（动态计算）
			tpoCanvasReady: false, // TPO Canvas是否已初始化
			tpoFontSize: 14, // TPO字体大小（动态计算，最大14px）
			tpoColWidth: 16, // TPO列宽（字体大小+2px）
			tpoPadding: { top: 10, right: 10, bottom: 30, left: 48 },
			
			// TPO十字标线
			showTPOCrossHair: false,
			tpoCrossHairX: 0,
			tpoCrossHairY: 0
		};
	},
	
	computed: {
		/**
		 * 倒序的时间列表（用于时间选择器显示）
		 */
		reversedTimeList() {
			return [...this.timeList].reverse();
		}
	},
	
	onLoad(options) {
		// 加载TPO设置
		this.loadTPOSettings();
		
		// 加载查询设置
		this.loadQuerySettings();
		
		// 从历史记录恢复
		if (options.restore) {
			this.restoreFromHistory();
		}
	},
	
	watch: {
		// 监听数据长度变化
		dataLength(newVal) {
			// 保存查询设置
			this.saveQuerySettings();
			// 如果已经查询过，重新查询
			if (this.hasData) {
				this.queryKLine();
			}
		}
	},
	
	onShow() {
		// 每次显示时检查是否需要恢复历史查询参数
		this.restoreFromHistory();
	},
	
	onReady() {
		// Canvas 2D 会在数据加载完成后、Canvas 元素渲染时初始化
		// 因为 Canvas 在 v-if="hasData" 条件下，所以不在这里初始化
	},
	
		methods: {
	/**
	 * 初始化 Canvas
	 * App平台使用旧版API，小程序平台使用Canvas 2D API
	 * @param {Number} retryCount - 重试次数
	 */
	initCanvas2D(retryCount = 0) {
		// #ifndef MP-WEIXIN
		// App平台使用旧版Canvas API
		const ctx = uni.createCanvasContext('klineChart', this);
		
		// 获取Canvas尺寸
		const query = uni.createSelectorQuery().in(this);
		query.select('#klineChart')
			.boundingClientRect(rect => {
				if (rect) {
					const dpr = uni.getSystemInfoSync().pixelRatio;
					
					this.chartContext = ctx;
					this.canvasWidth = rect.width;
					this.canvasHeight = rect.height;
					this.dpr = dpr;
					this.canvasReady = true;
					
					console.log('Canvas 初始化成功（旧版API）', {
						width: rect.width,
						height: rect.height,
						dpr: dpr,
						platform: 'APP-PLUS'
					});
					
					// 如果已经有数据，立即绘制
					if (this.hasData && this.partList.length > 0) {
						console.log('Canvas 初始化完成，开始绘制图表');
						this.drawChart();
					}
				} else if (retryCount < 5) {
					console.warn(`Canvas 节点获取失败，${500}ms 后重试 (${retryCount + 1}/5)`);
					setTimeout(() => {
						this.initCanvas2D(retryCount + 1);
					}, 500);
				}
			})
			.exec();
		// #endif
		
		// #ifdef MP-WEIXIN
		// 小程序平台使用Canvas 2D API
		const query = uni.createSelectorQuery().in(this);
		query.select('#klineChart')
			.fields({ node: true, size: true })
			.exec((res) => {
				if (res && res[0] && res[0].node) {
					const canvas = res[0].node;
					
					// 检查canvas是否有getContext方法
					if (typeof canvas.getContext !== 'function') {
						console.error('Canvas节点不支持getContext方法', canvas);
						
						if (retryCount < 5) {
							console.warn(`Canvas API未就绪，${500}ms 后重试 (${retryCount + 1}/5)`);
							setTimeout(() => {
								this.initCanvas2D(retryCount + 1);
							}, 500);
						} else {
							uni.showToast({
								title: 'Canvas初始化失败，请重启应用',
								icon: 'none',
								duration: 3000
							});
						}
						return;
					}
					
					const ctx = canvas.getContext('2d');
					
					if (!ctx) {
						console.error('获取Canvas上下文失败');
						if (retryCount < 5) {
							setTimeout(() => {
								this.initCanvas2D(retryCount + 1);
							}, 500);
						}
						return;
					}
					
					// 获取设备像素比
					const dpr = uni.getSystemInfoSync().pixelRatio;
					
					// 设置 canvas 实际大小（考虑设备像素比）
					canvas.width = res[0].width * dpr;
					canvas.height = res[0].height * dpr;
					
					// 缩放上下文以匹配设备像素比
					ctx.scale(dpr, dpr);
					
					// 保存上下文和画布信息
					this.chartContext = ctx;
					this.canvasNode = canvas;
					this.canvasWidth = res[0].width;
					this.canvasHeight = res[0].height;
					this.dpr = dpr;
					this.canvasReady = true;
					
					console.log('Canvas 2D 初始化成功', {
						width: res[0].width,
						height: res[0].height,
						dpr: dpr,
						platform: uni.getSystemInfoSync().platform
					});
					
					// 如果已经有数据，立即绘制
					if (this.hasData && this.partList.length > 0) {
						console.log('Canvas 初始化完成，开始绘制图表');
						this.drawChart();
					}
				} else {
					// Canvas 节点获取失败，重试
					if (retryCount < 5) {
						console.warn(`Canvas 节点获取失败，${500}ms 后重试 (${retryCount + 1}/5)`);
						setTimeout(() => {
							this.initCanvas2D(retryCount + 1);
						}, 500);
					} else {
						console.error('Canvas 节点获取失败，已达最大重试次数');
						uni.showToast({
							title: 'Canvas初始化失败',
							icon: 'none'
						});
					}
				}
			});
		// #endif
	},
		
		/**
		 * 搜索输入处理
		 * 1秒后稳定未改变时调用搜索接口
		 */
		onSearchInput(e) {
			const value = e.detail.value;
			this.searchKeyword = value;
			
			// 如果之前选中了股票，清除选中状态
			if (this.stockSelected) {
				this.stockSelected = false;
				this.selectedStock = null;
			}
			
			// 清除之前的定时器
			if (this.searchTimer) {
				clearTimeout(this.searchTimer);
			}
			
			// 如果输入为空，隐藏列表
			if (!value.trim()) {
				this.showAutocomplete = false;
				this.autocompleteList = [];
				return;
			}
			
			// 1秒后执行搜索
			this.searchTimer = setTimeout(() => {
				this.searchStocks(value);
			}, 1000);
		},
		
		/**
		 * 搜索股票
		 * @param {String} keyword - 搜索关键词
		 */
		async searchStocks(keyword) {
			try {
				const result = await api.searchStock(keyword);
				if (result.code === 0) {
					this.autocompleteList = result.stockList;
					this.showAutocomplete = this.autocompleteList.length > 0;
				}
			} catch (error) {
				console.error('搜索失败:', error);
				uni.showToast({
					title: '搜索失败',
					icon: 'none'
				});
			}
		},
		
		/**
		 * 选择股票
		 * @param {Object} stock - 股票信息
		 */
		selectStock(stock) {
			this.searchKeyword = stock.stockCode;
			this.selectedStock = stock;
			this.stockSelected = true;
			this.showAutocomplete = false;
			this.autocompleteList = [];
			
			// 保存选中的股票
			storage.saveSelectedStock(stock);
		},
		
		/**
		 * 清除搜索
		 */
		clearSearch() {
			this.searchKeyword = '';
			this.stockSelected = false;
			this.selectedStock = null;
			this.showAutocomplete = false;
			this.autocompleteList = [];
		},
		
		/**
		 * 复权方式改变
		 */
		onAdjustChange(e) {
			this.adjustIndex = e.detail.value;
			// 保存查询设置
			this.saveQuerySettings();
			// 如果已经查询过，重新查询
			if (this.hasData) {
				this.queryKLine();
			}
		},
		
		/**
		 * 切换周期
		 * @param {String} period - 周期值
		 */
		switchPeriod(period) {
			this.currentPeriod = period;
			// 保存查询设置
			this.saveQuerySettings();
			// 如果已经查询过，重新查询
			if (this.hasData) {
				this.queryKLine();
			}
		},
		
		/**
		 * 查询K线数据
		 */
		async queryKLine() {
			if (!this.stockSelected || !this.selectedStock) {
				uni.showToast({
					title: '请先选择股票',
					icon: 'none'
				});
				return;
			}
			
			if (this.dataLength < 1 || this.dataLength > 1023) {
				uni.showToast({
					title: '数据长度必须在1-1023之间',
					icon: 'none'
				});
				return;
			}
			
			this.loading = true;
			
			try {
				// 准备请求参数（固定请求160条数据）
				const params = {
					stockCode: this.selectedStock.stockCode,
					period: this.currentPeriod,
					count: 160
				};
				
				// 添加复权参数（仅对日K/周K/月K有效）
				if (['day', 'week', 'month'].includes(this.currentPeriod)) {
					const adjustMap = ['qfq', 'qfq', 'hfq'];
					params.adjust = adjustMap[this.adjustIndex];
				}
				
				// 调用API
				const result = await api.getKLineData(params);
				
				if (result.code === 0) {
					this.stockData = result.stockData;
					
					// 保存完整数据（160条）
					this.fullPartList = result.partList;
					
					// 提取时间列表（倒序显示）
					this.timeList = result.partList.map(item => this.formatTime(item[0]));
					
					// 默认选择最后一个时间（即最新的时间）
					this.selectedTimeIndex = this.fullPartList.length - 1;
					this.selectedTime = this.formatDetailTime(this.fullPartList[this.selectedTimeIndex][0]);
					
					// 提取数据（根据选择时间和数据长度）
					this.updateChartData();
					
					this.hasData = true;
					
					console.log('数据加载完成，partList长度:', this.partList.length);
					
					// 保存历史记录
					storage.saveHistory(this.stockData, {
						period: this.currentPeriod,
						adjust: this.adjustIndex,
						dataLength: this.dataLength
					});
					
				// 等待 Canvas 元素渲染后初始化并绘制
				// App平台需要更长的延迟时间来准备Canvas
				this.$nextTick(() => {
					setTimeout(() => {
						console.log('开始初始化 Canvas...');
						this.initCanvas2D();
						
						// 同时初始化TPO Canvas（间隔更长以避免资源竞争）
						setTimeout(() => {
							console.log('开始初始化 TPO Canvas...');
							this.initTPOCanvas2D();
						}, 500);
					}, 300);
				});
				}
			} catch (error) {
				console.error('查询失败:', error);
				uni.showToast({
					title: error.msg || '查询失败',
					icon: 'none'
				});
			} finally {
				this.loading = false;
			}
		},
		
		/**
		 * 更新图表数据
		 * 根据选择的时间和数据长度提取数据
		 */
		updateChartData() {
			const endIndex = this.selectedTimeIndex + 1;
			const startIndex = Math.max(0, endIndex - this.dataLength);
			this.partList = this.fullPartList.slice(startIndex, endIndex);
		},
		
		/**
		 * 显示时间选择器
		 */
		showTimePicker() {
			this.tempSelectedIndex = this.selectedTimeIndex;
			this.pickerVisible = true;
		},
		
		/**
		 * 隐藏时间选择器
		 */
		hideTimePicker() {
			this.pickerVisible = false;
		},
		
		/**
		 * 选择时间
		 * @param {Number} index - 时间索引
		 */
		selectTime(index) {
			this.tempSelectedIndex = index;
		},
		
		/**
		 * 确认时间选择
		 */
		confirmTime() {
			this.selectedTimeIndex = this.tempSelectedIndex;
			this.selectedTime = this.formatDetailTime(this.fullPartList[this.selectedTimeIndex][0]);
			this.pickerVisible = false;
			
			// 更新图表数据
			this.updateChartData();
			
			// 等待 DOM 更新后绘制K线图和TPO图
			this.$nextTick(() => {
				if (this.canvasReady) {
					this.drawChart();
				}
				
				// 重新初始化TPO Canvas（因为数据变化，高度可能需要调整）
				if (this.tpoCanvasReady) {
					setTimeout(() => {
						this.initTPOCanvas2D();
					}, 100);
				}
			});
		},
		
		/**
		 * 绘制K线图（Canvas 2D 模式）
		 */
		drawChart() {
			if (!this.canvasReady || !this.chartContext || this.partList.length === 0) {
				console.warn('绘制失败', {
					canvasReady: this.canvasReady,
					hasContext: !!this.chartContext,
					dataLength: this.partList.length
				});
				return;
			}
			
			console.log('开始绘制K线图', {
				dataLength: this.partList.length,
				canvasWidth: this.canvasWidth,
				canvasHeight: this.canvasHeight
			});
			
			const ctx = this.chartContext;
			const width = this.canvasWidth || 350; // canvas宽度
			const height = this.canvasHeight || 250; // canvas高度
			const padding = this.klinePadding;
			const chartWidth = width - padding.left - padding.right;
			const chartHeight = height - padding.top - padding.bottom;
			
			// 清空画布
			ctx.clearRect(0, 0, width, height);
			
			// 获取价格范围
			let minPrice = Infinity;
			let maxPrice = -Infinity;
			this.partList.forEach(item => {
				minPrice = Math.min(minPrice, item[4]); // 最低价
				maxPrice = Math.max(maxPrice, item[3]); // 最高价
			});
			
			// 添加一些边距
			const priceRange = maxPrice - minPrice;
			minPrice -= priceRange * 0.1;
			maxPrice += priceRange * 0.1;
			
			// 绘制背景网格
			ctx.strokeStyle = '#f0f0f0';
			ctx.lineWidth = 1;
			for (let i = 0; i <= 4; i++) {
				const y = padding.top + (chartHeight / 4) * i;
				ctx.beginPath();
				ctx.moveTo(padding.left, y);
				ctx.lineTo(padding.left + chartWidth, y);
				ctx.stroke();
			}
			
			// 绘制Y轴刻度和价格
			ctx.fillStyle = '#666';
			ctx.font = '10px sans-serif';
			ctx.textAlign = 'left';
			ctx.textBaseline = 'middle';
			for (let i = 0; i <= 4; i++) {
				const price = maxPrice - (maxPrice - minPrice) * (i / 4);
				const y = padding.top + (chartHeight / 4) * i;
				ctx.fillText(price.toFixed(2), 5, y);
			}
			
			// 计算每个K线的宽度
			const barWidth = chartWidth / this.partList.length;
			const barBodyWidth = Math.max(barWidth * 0.6, 2);
			
			// 绘制K线
			this.partList.forEach((item, index) => {
				const time = item[0];
				const open = item[1];
				const close = item[2];
				const high = item[3];
				const low = item[4];
				
				const x = padding.left + index * barWidth + barWidth / 2;
				
				// 计算y坐标，并限制在有效范围内
				let yHigh = padding.top + (1 - (high - minPrice) / (maxPrice - minPrice)) * chartHeight;
				let yLow = padding.top + (1 - (low - minPrice) / (maxPrice - minPrice)) * chartHeight;
				let yOpen = padding.top + (1 - (open - minPrice) / (maxPrice - minPrice)) * chartHeight;
				let yClose = padding.top + (1 - (close - minPrice) / (maxPrice - minPrice)) * chartHeight;
				
				// 确保y坐标在有效范围内
				yHigh = Math.max(padding.top, Math.min(yHigh, height - padding.bottom));
				yLow = Math.max(padding.top, Math.min(yLow, height - padding.bottom));
				yOpen = Math.max(padding.top, Math.min(yOpen, height - padding.bottom));
				yClose = Math.max(padding.top, Math.min(yClose, height - padding.bottom));
				
				// 确保x坐标在有效范围内
				if (x >= padding.left && x <= width - padding.right) {
					// 判断涨跌
					const isRise = close >= open;
					const color = isRise ? '#ef4444' : '#10b981';
					
					// 绘制上下影线
					ctx.strokeStyle = color;
					ctx.lineWidth = 1;
					ctx.beginPath();
					ctx.moveTo(x, yHigh);
					ctx.lineTo(x, yLow);
					ctx.stroke();
					
					// 绘制K线实体
					ctx.fillStyle = color;
					const bodyHeight = Math.abs(yClose - yOpen);
					const bodyY = Math.min(yOpen, yClose);
					ctx.fillRect(x - barBodyWidth / 2, bodyY, barBodyWidth, Math.max(bodyHeight, 1));
				}
			});
			
		// 绘制X轴时间标签（从最后一个坐标开始标）
		ctx.fillStyle = '#666';
		ctx.font = '9px sans-serif';
		ctx.textAlign = 'center';
		ctx.textBaseline = 'top';
		const timeStep = Math.ceil(this.partList.length / 5);
		// 从最后一个坐标开始向前标注
		for (let i = this.partList.length - 1; i >= 0; i -= timeStep) {
			const time = this.formatTime(this.partList[i][0]);
			const x = padding.left + i * barWidth + barWidth / 2;
			ctx.fillText(time, x, height - 20);
		}
			
	// 绘制十字标线
	if (this.showKLineCrossHair) {
		const crossX = this.klineCrossHairX;
		const crossY = this.klineCrossHairY;
		
		// 绘制垂直线（细黑实线）
		ctx.strokeStyle = '#000000';
		ctx.lineWidth = 0.5;
		// #ifdef MP-WEIXIN
		ctx.setLineDash([]);
		// #endif
		ctx.beginPath();
		ctx.moveTo(crossX, padding.top);
		ctx.lineTo(crossX, height - padding.bottom);
		ctx.stroke();
			
			// 绘制水平线（细黑实线）
			ctx.beginPath();
			ctx.moveTo(padding.left, crossY);
			ctx.lineTo(width - padding.right, crossY);
			ctx.stroke();
			
			// 绘制y轴价格标签
			if (crossY >= padding.top && crossY <= height - padding.bottom) {
				// 计算十字标线对应的价格
				const priceAtCross = maxPrice - (crossY - padding.top) / chartHeight * (maxPrice - minPrice);
				
				// 绘制背景框
				const labelText = priceAtCross.toFixed(2);
				ctx.font = '10px sans-serif';
				const textWidth = ctx.measureText(labelText).width;
				const labelPadding = 4;
				const labelX = 5;
				const labelY = crossY;
				
				ctx.fillStyle = 'rgba(102, 126, 234, 0.6)';
				ctx.fillRect(labelX, labelY - 10, textWidth + labelPadding * 2, 20);
				
				// 绘制价格文字
				ctx.fillStyle = '#000';
				ctx.textAlign = 'left';
				ctx.textBaseline = 'middle';
				ctx.fillText(labelText, labelX + labelPadding, labelY);
			}
			
			// 绘制x轴日期标签
			if (crossX >= padding.left && crossX <= width - padding.right) {
				// 计算十字标线对应的K线索引
				const touchX = crossX - padding.left;
				const barWidth = chartWidth / this.partList.length;
				const index = Math.floor(touchX / barWidth);
				
				if (index >= 0 && index < this.partList.length) {
					// 获取对应的时间
					const timeStr = this.formatTime(this.partList[index][0]);
					
					// 绘制背景框
					ctx.font = '10px sans-serif';
					const textWidth = ctx.measureText(timeStr).width;
					const labelPadding = 4;
					const labelX = crossX;
					const labelY = height - padding.bottom + 5;
					
					ctx.fillStyle = 'rgba(102, 126, 234, 0.6)';
					ctx.fillRect(labelX - textWidth / 2 - labelPadding, labelY, textWidth + labelPadding * 2, 20);
					
					// 绘制日期文字
					ctx.fillStyle = '#000';
					ctx.textAlign = 'center';
					ctx.textBaseline = 'top';
					ctx.fillText(timeStr, labelX, labelY + labelPadding);
				}
		}
	}
		
		// #ifndef MP-WEIXIN
		// App平台使用旧版Canvas API，需要调用draw()来显示绘制内容
		ctx.draw();
		// #endif
		
		console.log('K线图绘制完成');
	},
		
		/**
		 * 格式化时间（简短版，用于图表显示）
		 * @param {String} timeStr - 时间字符串
		 * @returns {String} 格式化后的时间
		 */
		formatTime(timeStr) {
			// 格式：YYYYMMddHHmmss 或 YYYY-MM-dd
			if (timeStr.length === 12) {
				// YYYYMMddHHmmss
				const month = timeStr.substring(4, 6);
				const day = timeStr.substring(6, 8);
				const hour = timeStr.substring(8, 10);
				const minute = timeStr.substring(10, 12);
				return `${month}/${day} ${hour}:${minute}`;
			} else if (timeStr.includes('-')) {
				// YYYY-MM-dd
				const parts = timeStr.split('-');
				return `${parts[1]}/${parts[2]}`;
			}
			return timeStr;
		},
		
		/**
		 * 格式化详细时间（完整版，用于时间选择器）
		 * @param {String} timeStr - 时间字符串
		 * @returns {String} 格式化后的时间
		 */
		formatDetailTime(timeStr) {
			// 格式：YYYYMMddHHmmss 或 YYYY-MM-dd
			if (timeStr.length === 12) {
				// YYYYMMddHHmmss
				const year = timeStr.substring(0, 4);
				const month = timeStr.substring(4, 6);
				const day = timeStr.substring(6, 8);
				const hour = timeStr.substring(8, 10);
				const minute = timeStr.substring(10, 12);
				return `${year}年${month}月${day}日 ${hour}:${minute}`;
			} else if (timeStr.includes('-')) {
				// YYYY-MM-dd
				const parts = timeStr.split('-');
				return `${parts[0]}年${parts[1]}月${parts[2]}日`;
			}
			return timeStr;
		},
		
		/**
		 * 格式化TPO第一个时间标签（MM/dd HH:mm）
		 * @param {String} timeStr - 时间字符串
		 * @returns {String} 格式化后的时间
		 */
		formatTPOFirstTime(timeStr) {
			// 格式：YYYYMMddHHmmss 或 YYYY-MM-dd
			if (timeStr.length === 12) {
				// YYYYMMddHHmmss
				const month = timeStr.substring(4, 6);
				const day = timeStr.substring(6, 8);
				const hour = timeStr.substring(8, 10);
				const minute = timeStr.substring(10, 12);
				return `${month}/${day} ${hour}:${minute}`;
			} else if (timeStr.includes('-')) {
				// YYYY-MM-dd（日K线，只显示日期）
				const parts = timeStr.split('-');
				return `${parts[1]}/${parts[2]}`;
			}
			return timeStr;
		},
		
		/**
		 * 格式化TPO其他时间标签（HH:mm）
		 * @param {String} timeStr - 时间字符串
		 * @returns {String} 格式化后的时间
		 */
		formatTPOTime(timeStr) {
			// 格式：YYYYMMddHHmmss 或 YYYY-MM-dd
			if (timeStr.length === 12) {
				// YYYYMMddHHmmss
				const hour = timeStr.substring(8, 10);
				const minute = timeStr.substring(10, 12);
				return `${hour}:${minute}`;
			} else if (timeStr.includes('-')) {
				// YYYY-MM-dd（日K线，显示日期）
				const parts = timeStr.split('-');
				return `${parts[1]}/${parts[2]}`;
			}
			return timeStr;
		},
		
		/**
		 * 格式化TPO十字标线时间（根据当前周期判断）
		 * @param {String} timeStr - 时间字符串
		 * @returns {String} 格式化后的时间
		 */
		formatTPOCrossHairTime(timeStr) {
			// 格式：YYYYMMddHHmmss 或 YYYY-MM-dd
			if (timeStr.length === 12) {
				// YYYYMMddHHmmss - 分钟级别数据
				const month = timeStr.substring(4, 6);
				const day = timeStr.substring(6, 8);
				const hour = timeStr.substring(8, 10);
				const minute = timeStr.substring(10, 12);
				
				// 如果是5分、30分、60分，显示完整时间 MM/dd HH:mm
				if (['m5', 'm30', 'm60'].includes(this.currentPeriod)) {
					return `${month}/${day} ${hour}:${minute}`;
				} else {
					// 其他周期只显示时间
					return `${hour}:${minute}`;
				}
			} else if (timeStr.includes('-')) {
				// YYYY-MM-dd（日K线、周K线、月K线）
				const parts = timeStr.split('-');
				return `${parts[1]}/${parts[2]}`;
			}
			return timeStr;
		},
		
		/**
		 * 格式化成交量
		 * @param {Number} volume - 成交量
		 * @returns {String} 格式化后的成交量
		 */
		formatVolume(volume) {
			if (volume >= 100000000) {
				return (volume / 100000000).toFixed(2) + '亿';
			} else if (volume >= 10000) {
				return (volume / 10000).toFixed(2) + '万';
			}
			return volume.toFixed(2);
		},
		
		/**
		 * 格式化成交额
		 * @param {Number} turnover - 成交额（元）
		 * @returns {String} 格式化后的成交额
		 */
		formatTurnover(turnover) {
			if (turnover >= 100000000) {
				return (turnover / 100000000).toFixed(2) + '亿';
			} else if (turnover >= 10000) {
				return (turnover / 10000).toFixed(2) + '万';
			}
			return turnover.toFixed(2);
		},
		
		/**
		 * 从历史记录恢复查询参数
		 */
		restoreFromHistory() {
			const stock = storage.getSelectedStock();
			if (stock) {
				// 恢复股票信息
				this.selectStock(stock);
				
				// 如果有查询参数，则恢复并自动查询
				if (stock.queryParams) {
					const params = stock.queryParams;
					
					// 恢复周期
					if (params.period) {
						this.currentPeriod = params.period;
					}
					
					// 恢复复权方式
					if (params.adjust !== undefined) {
						this.adjustIndex = params.adjust;
					}
					
					// 恢复数据长度
					if (params.dataLength) {
						this.dataLength = params.dataLength;
					}
					
					// 清除选中的股票，防止重复恢复
					storage.saveSelectedStock({
						stockCode: stock.stockCode,
						stockName: stock.stockName,
						stockNumber: stock.stockNumber
					});
					
					// 自动执行查询
					this.$nextTick(() => {
						this.queryKLine();
					});
				}
			}
		},
		
		/**
		 * 跳转到历史页面
		 */
		goToHistory() {
			uni.navigateTo({
				url: '/pages/history/history'
			});
		},
		
		/**
		 * 图表触摸开始
		 * @param {Object} e - 触摸事件
		 */
		onChartTouchStart(e) {
			const touch = e.touches[0];
			this.touchStartX = touch.x;
			this.klineCrossHairX = touch.x;
			this.klineCrossHairY = touch.y;
			this.showKLineCrossHair = true;
			this.showKLineDetailAtPosition(touch.x, touch.y);
		},
		
		/**
		 * 图表触摸移动
		 * @param {Object} e - 触摸事件
		 */
		onChartTouchMove(e) {
			const touch = e.touches[0];
			this.klineCrossHairX = touch.x;
			this.klineCrossHairY = touch.y;
			this.showKLineDetailAtPosition(touch.x, touch.y);
		},
		
		/**
		 * 图表触摸结束
		 * @param {Object} e - 触摸事件
		 */
		onChartTouchEnd(e) {
			// 隐藏十字标线
			this.showKLineCrossHair = false;
			
			// 松手后1秒后隐藏详情
			if (this.detailTimer) {
				clearTimeout(this.detailTimer);
			}
			this.detailTimer = setTimeout(() => {
				this.showKLineDetail = false;
			}, 1000);
		},
		
		/**
		 * 在指定位置显示K线详情
		 * @param {Number} x - 触摸点X坐标
		 * @param {Number} y - 触摸点Y坐标
		 */
		showKLineDetailAtPosition(x, y) {
			if (this.partList.length === 0) {
				return;
			}
			
			// 计算图表区域（需要与drawChart中的padding保持一致）
			const padding = this.klinePadding;
			const canvasWidth = this.canvasWidth || 350;
			const chartWidth = canvasWidth - padding.left - padding.right;
			
			// 计算触摸点对应的K线索引
			const touchX = x - padding.left;
			if (touchX < 0 || touchX > chartWidth) {
				return;
			}
			
			const barWidth = chartWidth / this.partList.length;
			const index = Math.floor(touchX / barWidth);
			
			if (index >= 0 && index < this.partList.length) {
				const item = this.partList[index];
				this.klineDetail = {
					time: this.formatDetailTime(item[0]),
					open: item[1].toFixed(2),
					close: item[2].toFixed(2),
					high: item[3].toFixed(2),
					low: item[4].toFixed(2),
					volume: item[5]
				};
				this.showKLineDetail = true;
				
				// 清除之前的定时器
				if (this.detailTimer) {
					clearTimeout(this.detailTimer);
				}
				
				// 重新绘制图表以显示十字标线
				if (this.showKLineCrossHair) {
					this.drawChart();
				}
			}
		},
		
		// ==================== TPO（四度空间）相关方法 ====================
		
		/**
		 * 分度值改变事件
		 * 失去焦点时触发，重新初始化Canvas（因为高度需要重新计算）
		 */
		onTpoTickSizeChange() {
			// 转换为数字并验证
			let tickSize = parseFloat(this.tpoTickSize);
			
			if (!tickSize || tickSize <= 0 || isNaN(tickSize)) {
				uni.showToast({
					title: '分度值必须大于0',
					icon: 'none'
				});
				this.tpoTickSize = 0.191;
				return;
			}
			
			// 限制最多3位小数
			tickSize = Math.round(tickSize * 1000) / 1000;
			this.tpoTickSize = tickSize;
			
			// 保存TPO设置
			this.saveTPOSettings();
			
			// 验证分度值是否会导致过多分度线
			if (this.partList.length > 0) {
				const priceRange = this.getTPOPriceRange();
				if (priceRange) {
					const { maxPrice, minPrice } = priceRange;
					const range = maxPrice - minPrice;
					const estimatedLines = Math.ceil(range / this.tpoTickSize);
					
					// 建议最大分度线数量为800条（约12800px高度）
					if (estimatedLines > 800) {
						const suggestedTickSize = (range / 800).toFixed(3);
						uni.showModal({
							title: '分度值过小',
							content: `当前分度值会产生${estimatedLines}条分度线，可能导致显示异常。建议使用${suggestedTickSize}或更大的值。`,
							showCancel: true,
							confirmText: '使用建议值',
							cancelText: '继续',
							success: (res) => {
								if (res.confirm) {
									this.tpoTickSize = parseFloat(suggestedTickSize);
									// 保存更新后的设置
									this.saveTPOSettings();
								}
								// 无论如何都重新初始化
								this.$nextTick(() => {
									if (this.tpoCanvasReady) {
										this.initTPOCanvas2D();
									}
								});
							}
						});
						return;
					}
				}
			}
			
			// 重新初始化TPO Canvas（因为分度值变化，高度需要重新计算）
			this.$nextTick(() => {
				if (this.tpoCanvasReady) {
					this.initTPOCanvas2D();
				}
			});
		},
		
		/**
		 * 填充值改变事件
		 * @param {Object} e - 事件对象
		 */
		onTpoFillChange(e) {
			this.tpoFillIndex = e.detail.value;
			
			// 保存TPO设置
			this.saveTPOSettings();
			
			// 重新绘制TPO图
			this.$nextTick(() => {
				if (this.tpoCanvasReady) {
					this.drawTPOChart();
				}
			});
		},
		
	/**
	 * 初始化TPO Canvas
	 * App平台使用旧版API，小程序平台使用Canvas 2D API
	 * @param {Number} retryCount - 重试次数
	 */
	initTPOCanvas2D(retryCount = 0) {
		// #ifndef MP-WEIXIN
		// App平台使用旧版Canvas API
		const ctx = uni.createCanvasContext('tpoChart', this);
		
		// 获取Canvas尺寸
		const query = uni.createSelectorQuery().in(this);
		query.select('#tpoChart')
			.boundingClientRect(rect => {
				if (rect) {
					const dpr = uni.getSystemInfoSync().pixelRatio;
					
					// 保存画布信息
					this.tpoCanvasWidth = rect.width;
					this.dpr = dpr;
					
					// 先计算需要的高度
					const calculatedHeight = this.calculateTPOHeight();
					this.tpoCanvasHeight = calculatedHeight;
					
					// 保存上下文和画布信息
					this.tpoContext = ctx;
					this.tpoCanvasReady = true;
					
					console.log('TPO Canvas 初始化成功（旧版API）', {
						width: rect.width,
						height: calculatedHeight,
						dpr: dpr,
						platform: 'APP-PLUS'
					});
					
					// 立即绘制TPO图
					this.drawTPOChart();
				} else if (retryCount < 5) {
					console.warn(`TPO Canvas 节点获取失败，${500}ms 后重试 (${retryCount + 1}/5)`);
					setTimeout(() => {
						this.initTPOCanvas2D(retryCount + 1);
					}, 500);
				}
			})
			.exec();
		// #endif
		
		// #ifdef MP-WEIXIN
		// 小程序平台使用Canvas 2D API
		const query = uni.createSelectorQuery().in(this);
		query.select('#tpoChart')
			.fields({ node: true, size: true })
			.exec((res) => {
				if (res && res[0] && res[0].node) {
					const canvas = res[0].node;
					
					// 检查canvas是否有getContext方法
					if (typeof canvas.getContext !== 'function') {
						console.error('TPO Canvas节点不支持getContext方法', canvas);
						
						if (retryCount < 5) {
							console.warn(`TPO Canvas API未就绪，${500}ms 后重试 (${retryCount + 1}/5)`);
							setTimeout(() => {
								this.initTPOCanvas2D(retryCount + 1);
							}, 500);
						} else {
							uni.showToast({
								title: 'TPO Canvas初始化失败',
								icon: 'none',
								duration: 3000
							});
						}
						return;
					}
					
					const ctx = canvas.getContext('2d');
					
					if (!ctx) {
						console.error('获取TPO Canvas上下文失败');
						if (retryCount < 5) {
							setTimeout(() => {
								this.initTPOCanvas2D(retryCount + 1);
							}, 500);
						}
						return;
					}
					
					// 获取设备像素比
					const dpr = uni.getSystemInfoSync().pixelRatio;
					
					// 保存画布信息（宽度先用屏幕宽度）
					this.tpoCanvasWidth = res[0].width;
					this.dpr = dpr;
					
					// 先计算需要的高度
					const calculatedHeight = this.calculateTPOHeight();
					this.tpoCanvasHeight = calculatedHeight;
					
					// 设置 canvas 实际大小（考虑设备像素比）
					canvas.width = res[0].width * dpr;
					canvas.height = calculatedHeight * dpr;
					
					// 缩放上下文以匹配设备像素比
					ctx.scale(dpr, dpr);
					
					// 保存上下文和画布信息
					this.tpoContext = ctx;
					this.tpoCanvasNode = canvas;
					this.tpoCanvasReady = true;
					
					console.log('TPO Canvas 2D 初始化成功', {
						width: res[0].width,
						height: calculatedHeight,
						dpr: dpr,
						platform: uni.getSystemInfoSync().platform
					});
					
					// 立即绘制TPO图
					this.drawTPOChart();
				} else {
					// Canvas 节点获取失败，重试
					if (retryCount < 5) {
						console.warn(`TPO Canvas 节点获取失败，${500}ms 后重试 (${retryCount + 1}/5)`);
						setTimeout(() => {
							this.initTPOCanvas2D(retryCount + 1);
						}, 500);
					} else {
						console.error('TPO Canvas 节点获取失败，已达最大重试次数');
						uni.showToast({
							title: 'TPO Canvas初始化失败',
							icon: 'none',
							duration: 3000
						});
					}
				}
			});
		// #endif
	},
		
		/**
		 * 计算TPO Canvas需要的高度
		 * 根据y轴分度线数量和动态字体大小计算
		 * @returns {Number} Canvas高度
		 */
		calculateTPOHeight() {
			if (this.partList.length === 0) {
				return 300;
			}
			
			// 计算价格区间
			const priceRange = this.getTPOPriceRange();
			if (!priceRange) {
				return 300;
			}
			
			// 计算分度线数量
			const tickLines = this.calculateTPOTickLines(priceRange);
			const tickCount = tickLines.length;
			
			// 根据数据长度计算字体大小和列宽
			this.calculateTPOFontSize();
			
			// 行高 = 字体大小 + 2px，最小12px
			const lineHeight = Math.max(this.tpoFontSize + 2, 12);
			
			// 计算高度：顶部padding + 分度线数量 * 行高 + 底部padding
			const padding = this.tpoPadding;
			let height = padding.top + tickCount * lineHeight + padding.bottom;
			
			// 限制最大高度为 16000px（留一些余量，避免接近16384的限制）
			// 如果超过限制，需要调整行高
			const MAX_HEIGHT = 1365;
			if (height > MAX_HEIGHT) {
				console.warn(`计算的TPO高度(${height}px)超过限制，自动调整为${MAX_HEIGHT}px`);
				
				// 重新计算行高：(最大高度 - padding) / 分度线数量
				const adjustedLineHeight = Math.max(
					Math.floor((MAX_HEIGHT - padding.top - padding.bottom) / tickCount),
					8 // 最小行高8px
				);
				
				// 同时调整字体大小
				this.tpoFontSize = Math.max(adjustedLineHeight - 2, 6);
				
				height = MAX_HEIGHT;
				
				uni.showToast({
					title: '分度值过小，图表已自动调整',
					icon: 'none',
					duration: 2000
				});
			}
			
			return Math.max(height, 100); // 最小高度100
		},
		
		/**
		 * 计算TPO字体大小和列宽
		 * 根据数据长度均等分宽度，最大14px
		 */
		calculateTPOFontSize() {
			const padding = this.tpoPadding;
			const chartWidth = this.tpoCanvasWidth - padding.left - padding.right;
			
			// 每个数据列的宽度 = 图表宽度 / 数据长度
			const colWidth = chartWidth / this.partList.length;
			
			// 字体大小 = 列宽 - 1px，最大14px
			const fontSize = Math.min(Math.floor(colWidth - 1), 14);
			
			// 确保字体大小至少为8px
			this.tpoFontSize = Math.max(fontSize, 6);
			
			// 列宽
			this.tpoColWidth = colWidth;
		},
		
		/**
		 * 获取TPO价格区间
		 * @returns {Object|null} { maxPrice, minPrice } 或 null
		 */
		getTPOPriceRange() {
			if (this.partList.length === 0) {
				return null;
			}
			
			let maxPrice = -Infinity;
			let minPrice = Infinity;
			
			this.partList.forEach(item => {
				const high = item[3]; // 最高价
				const low = item[4]; // 最低价
				maxPrice = Math.max(maxPrice, high);
				minPrice = Math.min(minPrice, low);
			});
			
			return { maxPrice, minPrice };
		},
		
		/**
		 * 计算TPO的y轴分度线
		 * @param {Object} priceRange - 价格区间 { maxPrice, minPrice }
		 * @returns {Array} 分度线数组，从大到小排列
		 */
		calculateTPOTickLines(priceRange) {
			const { maxPrice, minPrice } = priceRange;
			const tickSize = this.tpoTickSize;
			const tickLines = [];
			
			let currentPrice = maxPrice;
			while (currentPrice >= minPrice) {
				tickLines.push(currentPrice);
				currentPrice -= tickSize;
			}
			
			// 如果最后一个分度线大于最小价格，补充一个
			if (tickLines[tickLines.length - 1] > minPrice) {
				tickLines.push(currentPrice);
			}
			
			return tickLines;
		},
		
		/**
		 * 根据价格找到最近的分度线索引
		 * @param {Number} price - 价格
		 * @param {Array} tickLines - 分度线数组
		 * @returns {Number} 最近的分度线索引
		 */
		findNearestTickIndex(price, tickLines) {
			let nearestIndex = 0;
			let minDistance = Infinity;
			
			for (let i = 0; i < tickLines.length; i++) {
				const distance = Math.abs(price - tickLines[i]);
				if (distance < minDistance) {
					minDistance = distance;
					nearestIndex = i;
				}
			}
			
			return nearestIndex;
		},
		
		/**
		 * 绘制TPO（市场轮廓图）
		 */
		drawTPOChart() {
			if (!this.tpoCanvasReady || !this.tpoContext || this.partList.length === 0) {
				console.warn('TPO绘制失败：Canvas未准备好或无数据');
				return;
			}
			
			console.log('开始绘制TPO图', {
				dataLength: this.partList.length,
				tickSize: this.tpoTickSize,
				fillType: this.tpoFillOptions[this.tpoFillIndex]
			});
			
			const ctx = this.tpoContext;
			const width = this.tpoCanvasWidth;
			const height = this.tpoCanvasHeight;
			const padding = this.tpoPadding;
			
			// 清空画布
			ctx.clearRect(0, 0, width, height);
			
			// 获取价格区间和分度线
			const priceRange = this.getTPOPriceRange();
			if (!priceRange) {
				return;
			}
			
			const tickLines = this.calculateTPOTickLines(priceRange);
			const tickCount = tickLines.length;
			
			// 计算绘图区域
			const chartWidth = width - padding.left - padding.right;
			const chartHeight = height - padding.top - padding.bottom;
			
			// 使用动态计算的行高：字体大小 + 2px
			const lineHeight = Math.max(this.tpoFontSize + 2, 12);
			
			// 获取当前填充字符列表
			const fillType = this.tpoFillOptions[this.tpoFillIndex];
			const fillChars = this.tpoFillCharsList[fillType];
			
			// 使用动态计算的列宽
			const colWidth = this.tpoColWidth;
			
			// 绘制背景网格和y轴刻度（横向分度线）
			ctx.strokeStyle = '#f0f0f0';
			ctx.lineWidth = 1;
			ctx.fillStyle = '#666';
			ctx.font = '10px sans-serif';
			ctx.textAlign = 'right';
			ctx.textBaseline = 'middle';
			
			for (let i = 0; i < tickCount; i++) {
				const y = padding.top + i * lineHeight;
				
				// 确保不超出canvas范围
				if (y >= padding.top && y <= height - padding.bottom) {
					// 绘制横向网格线
					ctx.beginPath();
					ctx.moveTo(padding.left, y);
					ctx.lineTo(padding.left + chartWidth, y);
					ctx.stroke();
					
					// 绘制价格标签
					ctx.fillText(tickLines[i].toFixed(3), padding.left - 5, y);
				}
			}
			
			// 绘制x轴网格线（纵向线）- 每个数据点都画一条
			this.partList.forEach((item, index) => {
				const x = padding.left + index * colWidth + colWidth / 2;
				
				// 确保不超出canvas范围
				if (x >= padding.left && x <= width - padding.right) {
					// 计算是否需要显示标签
					const timeStep = Math.ceil(this.partList.length / 6); // 显示约6个时间标签
					const isLabelPosition = index === 0 || // 第一个位置
						index === this.partList.length - 1 || // 最后一个位置
						index % timeStep === 0; // 其他标签位置
					
					// 有标签的位置使用2px宽度，其他位置使用1px
					ctx.lineWidth = isLabelPosition ? 1.5 : 1;
					ctx.strokeStyle = isLabelPosition ? '#e5e7eb' : '#f0f0f0';
					
					ctx.beginPath();
					ctx.moveTo(x, padding.top);
					ctx.lineTo(x, height - padding.bottom);
					ctx.stroke();
				}
			});
			
		// 绘制TPO字符（加粗字体）
		ctx.textAlign = 'center';
		ctx.textBaseline = 'middle';
		ctx.fillStyle = '#667eea';
			
		this.partList.forEach((item, index) => {
			const high = item[3]; // 最高价
			const low = item[4]; // 最低价
			
			// 找到最高价和最低价对应的分度线索引
			const highTickIndex = this.findNearestTickIndex(high, tickLines);
			const lowTickIndex = this.findNearestTickIndex(low, tickLines);
			
			// 确定起始和结束索引（从上到下）
			const startIndex = Math.min(highTickIndex, lowTickIndex);
			const endIndex = Math.max(highTickIndex, lowTickIndex);
			const charIndex = index % fillChars.length
			// 获取填充字符（循环使用）
			const fillChar = fillChars[charIndex];
			if(charIndex==0){
				ctx.font = `900 ${this.tpoFontSize}px sans-serif`;
			}else{
				ctx.font = `600 ${this.tpoFontSize}px sans-serif`;
			}
			
			// 计算x坐标
			const x = padding.left + index * colWidth + colWidth / 2;
			
			// 确保x不超出canvas范围
			if (x >= padding.left && x <= width - padding.right) {
				// 在该数据的价格区间内的所有分度线位置填充字符
				for (let tickIndex = startIndex; tickIndex <= endIndex; tickIndex++) {
					const y = padding.top + tickIndex * lineHeight;
					
					// 确保y不超出canvas范围
					if (y >= padding.top && y <= height - padding.bottom) {
						ctx.fillText(fillChar, x, y);
					}
				}
			}
		});
			
		// 绘制x轴时间标签
		ctx.fillStyle = '#666';
		ctx.font = '10px sans-serif';
		ctx.textAlign = 'center';
		ctx.textBaseline = 'top';
		
		const timeStep = Math.ceil(this.partList.length / 6); // 显示约6个时间标签
		
		// 绘制第一个标签（特殊格式：MM/dd HH:mm）
		if (this.partList.length > 0) {
			const firstTime = this.formatTPOFirstTime(this.partList[0][0]);
			const firstX = padding.left + colWidth / 2;
			ctx.fillText(firstTime, firstX, height - 25);
		}
		
		// 从第二个标签开始向后标注（格式：HH:mm）
		for (let i = timeStep; i < this.partList.length - 1; i += timeStep) {
			const time = this.formatTPOTime(this.partList[i][0]);
			const x = padding.left + i * colWidth + colWidth / 2;
			ctx.fillText(time, x, height - 25);
		}
		
		// 绘制最后一个标签（格式：HH:mm）
		if (this.partList.length > 1) {
			const lastTime = this.formatTPOTime(this.partList[this.partList.length - 1][0]);
			const lastX = padding.left + (this.partList.length - 1) * colWidth + colWidth / 2;
			ctx.fillText(lastTime, lastX, height - 25);
		}
			
	// 绘制十字标线
	if (this.showTPOCrossHair) {
		const crossX = this.tpoCrossHairX;
		const crossY = this.tpoCrossHairY;
		
		// 绘制垂直线（细黑实线）
		ctx.strokeStyle = '#000000';
		ctx.lineWidth = 0.5;
		// #ifdef MP-WEIXIN
		ctx.setLineDash([]);
		// #endif
		ctx.beginPath();
		ctx.moveTo(crossX, padding.top);
		ctx.lineTo(crossX, height - padding.bottom);
		ctx.stroke();
			
			// 绘制水平线（细黑实线）
			ctx.beginPath();
			ctx.moveTo(padding.left, crossY);
			ctx.lineTo(width - padding.right, crossY);
			ctx.stroke();
			
			// 绘制y轴价格标签
			if (crossY >= padding.top && crossY <= height - padding.bottom) {
				// 计算十字标线对应的价格
				const tickIndex = Math.floor((crossY - padding.top) / lineHeight);
				if (tickIndex >= 0 && tickIndex < tickLines.length) {
					const priceAtCross = tickLines[tickIndex];
					
					// 绘制背景框
					const labelText = priceAtCross.toFixed(3);
					ctx.font = '10px sans-serif';
					const textWidth = ctx.measureText(labelText).width;
					const labelPadding = 4;
					const labelX = 5;
					const labelY = crossY;
					
					ctx.fillStyle = 'rgba(102, 126, 234, 0.6)';
					ctx.fillRect(labelX, labelY - 10, textWidth + labelPadding * 2, 20);
					
					// 绘制价格文字
					ctx.fillStyle = '#000';
					ctx.textAlign = 'left';
					ctx.textBaseline = 'middle';
					ctx.fillText(labelText, labelX + labelPadding, labelY);
				}
			}
			
			// 绘制x轴日期标签
			if (crossX >= padding.left && crossX <= width - padding.right) {
				// 计算十字标线对应的数据索引
				const touchX = crossX - padding.left;
				const index = Math.floor(touchX / colWidth);
				
				if (index >= 0 && index < this.partList.length) {
					// 获取对应的时间（根据周期判断显示格式）
					const timeStr = this.formatTPOCrossHairTime(this.partList[index][0]);
					
					// 绘制背景框
					ctx.font = '10px sans-serif';
					const textWidth = ctx.measureText(timeStr).width;
					const labelPadding = 4;
					const labelX = crossX;
					const labelY = height - padding.bottom + 5;
					
					ctx.fillStyle = 'rgba(102, 126, 234, 0.6)';
					ctx.fillRect(labelX - textWidth / 2 - labelPadding, labelY, textWidth + labelPadding * 2, 20);
					
					// 绘制日期文字
					ctx.fillStyle = '#000';
					ctx.textAlign = 'center';
					ctx.textBaseline = 'top';
					ctx.fillText(timeStr, labelX, labelY + labelPadding);
			}
		}
	}
		
		// #ifndef MP-WEIXIN
		// App平台使用旧版Canvas API，需要调用draw()来显示绘制内容
		ctx.draw();
		// #endif
		
		console.log('TPO图绘制完成');
	},
		
		/**
		 * TPO图触摸开始
		 * @param {Object} e - 触摸事件
		 */
		onTPOTouchStart(e) {
			// 如果已经显示了十字标线，则隐藏它
			if (this.showTPOCrossHair) {
				this.hideTPOCrossHair();
				return;
			}
			
			const touch = e.touches[0];
			this.tpoCrossHairX = touch.x;
			this.tpoCrossHairY = touch.y;
			this.showTPOCrossHair = true;
			
			// 重新绘制TPO图以显示十字标线
			this.drawTPOChart();
		},
		
		/**
		 * 隐藏TPO图十字标线
		 */
		hideTPOCrossHair() {
			this.showTPOCrossHair = false;
			this.drawTPOChart();
		},
		
		/**
		 * TPO图触摸移动
		 * @param {Object} e - 触摸事件
		 */
		onTPOTouchMove(e) {
			const touch = e.touches[0];
			this.tpoCrossHairX = touch.x;
			this.tpoCrossHairY = touch.y;
			
			// 重新绘制TPO图以显示十字标线
			this.drawTPOChart();
		},
		
		/**
		 * TPO图触摸结束
		 * @param {Object} e - 触摸事件
		 */
		onTPOTouchEnd(e) {
			// 保持显示十字标线，不做任何处理
		},
		
		// ==================== TPO设置持久化相关方法 ====================
		
		/**
		 * 加载TPO设置
		 * 从本地存储加载分度值和填充值设置
		 */
		loadTPOSettings() {
			const settings = storage.getTPOSettings();
			if (settings) {
				// 回显分度值
				if (settings.tpoTickSize !== undefined && settings.tpoTickSize > 0) {
					this.tpoTickSize = settings.tpoTickSize;
				}
				
				// 回显填充值索引
				if (settings.tpoFillIndex !== undefined) {
					this.tpoFillIndex = settings.tpoFillIndex;
				}
				
				console.log('TPO设置已加载:', settings);
			}
		},
		
		/**
		 * 保存TPO设置
		 * 将当前分度值和填充值保存到本地存储
		 */
		saveTPOSettings() {
			const settings = {
				tpoTickSize: this.tpoTickSize,
				tpoFillIndex: this.tpoFillIndex
			};
			storage.saveTPOSettings(settings);
			console.log('TPO设置已保存:', settings);
		},
		
		/**
		 * 保存查询设置
		 * 将当前的复权方式、数据长度和周期保存到本地存储
		 */
		saveQuerySettings() {
			const settings = {
				adjustIndex: this.adjustIndex,
				dataLength: this.dataLength,
				period: this.currentPeriod
			};
			storage.saveQuerySettings(settings);
			console.log('查询设置已保存:', settings);
		},
		
		/**
		 * 加载查询设置
		 * 从本地存储加载复权方式、数据长度和周期设置
		 */
		loadQuerySettings() {
			const settings = storage.getQuerySettings();
			if (settings) {
				// 回显复权方式
				if (settings.adjustIndex !== undefined) {
					this.adjustIndex = settings.adjustIndex;
				}
				
				// 回显数据长度
				if (settings.dataLength !== undefined && settings.dataLength >= 1 && settings.dataLength <= 1023) {
					this.dataLength = settings.dataLength;
				}
				
				// 回显周期
				if (settings.period) {
					this.currentPeriod = settings.period;
				}
				
				console.log('查询设置已加载:', settings);
			}
		},
		
		/**
		 * 将ArrayBuffer转换为Base64字符串
		 * @param {ArrayBuffer} buffer - 数组缓冲区
		 * @returns {String} Base64字符串
		 */
		arrayBufferToBase64(buffer) {
			let binary = '';
			const bytes = new Uint8Array(buffer);
			const len = bytes.byteLength;
			for (let i = 0; i < len; i++) {
				binary += String.fromCharCode(bytes[i]);
			}
			return uni.arrayBufferToBase64(buffer);
		},
		
		/**
		 * 导出TPO数据到Excel
		 * 按照x轴（时间）、y轴（价格）和对应的值（填充字符）导出
		 */
		exportTPOToExcel() {
			if (this.partList.length === 0) {
				uni.showToast({
					title: '没有数据可导出',
					icon: 'none'
				});
				return;
			}
			
			try {
				// 获取价格区间和分度线
				const priceRange = this.getTPOPriceRange();
				if (!priceRange) {
					uni.showToast({
						title: '数据错误',
						icon: 'none'
					});
					return;
				}
				
				const tickLines = this.calculateTPOTickLines(priceRange);
				const fillType = this.tpoFillOptions[this.tpoFillIndex];
				const fillChars = this.tpoFillCharsList[fillType];
				
				// 构建Excel数据
				// 第一行：标题行（价格 + 各时间点）
				const header = ['价格'];
				this.partList.forEach((item, index) => {
					const timeStr = index === 0 ? 
						this.formatTPOFirstTime(item[0]) : 
						this.formatTPOTime(item[0]);
					header.push(timeStr);
				});
				
				// 数据行：每行代表一个价格分度线
				const data = [header];
				tickLines.forEach((price, tickIndex) => {
					const row = [price.toFixed(3)];
					
					// 对于每个时间点，判断是否在该价格分度线上有值
					this.partList.forEach((item, dataIndex) => {
						const high = item[3]; // 最高价
						const low = item[4]; // 最低价
						
						// 找到最高价和最低价对应的分度线索引
						const highTickIndex = this.findNearestTickIndex(high, tickLines);
						const lowTickIndex = this.findNearestTickIndex(low, tickLines);
						
						const startIndex = Math.min(highTickIndex, lowTickIndex);
						const endIndex = Math.max(highTickIndex, lowTickIndex);
						
						// 如果当前价格分度线在该数据的价格区间内，填充字符
						if (tickIndex >= startIndex && tickIndex <= endIndex) {
							const fillChar = fillChars[dataIndex % fillChars.length];
							row.push(fillChar);
						} else {
							row.push('');
						}
					});
					
					data.push(row);
				});
				
				// 创建工作表
				const ws = XLSX.utils.aoa_to_sheet(data);
				
				// 设置列宽
				const colWidths = [{ wch: 12 }]; // 价格列宽度
				this.partList.forEach(() => {
					colWidths.push({ wch: 8 }); // 时间列宽度
				});
				ws['!cols'] = colWidths;
				
				// 创建工作簿
				const wb = XLSX.utils.book_new();
				XLSX.utils.book_append_sheet(wb, ws, 'TPO数据');
				
				// 生成Excel文件（使用二进制数组格式）
				const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
				
				// 保存文件
				const fileName = `TPO_${this.stockData.stockCode}_${Date.now()}.xlsx`;
				
				// 使用临时文件路径
				const fs = uni.getFileSystemManager();
				const tempFilePath = `${wx.env.USER_DATA_PATH}/${fileName}`;
				
				// 将ArrayBuffer转换为base64
				const base64 = this.arrayBufferToBase64(wbout);
				
				// 写入临时文件
				fs.writeFile({
					filePath: tempFilePath,
					data: base64,
					encoding: 'base64',
					success: () => {
						console.log('文件写入成功:', tempFilePath);
						
						// 保存到相册或分享
						uni.showModal({
							title: '导出成功',
							content: '请选择操作',
							confirmText: '打开文件',
							cancelText: '分享文件',
							success: (res) => {
								if (res.confirm) {
									// 打开文件
									uni.openDocument({
										filePath: tempFilePath,
										showMenu: true,
										fileType: 'xlsx',
										success: () => {
											console.log('Excel文件打开成功');
										},
										fail: (err) => {
											console.error('打开Excel文件失败:', err);
											uni.showToast({
												title: '打开失败，请尝试分享',
												icon: 'none'
											});
										}
									});
								} else if (res.cancel) {
									// 分享文件
									uni.shareFileMessage({
										filePath: tempFilePath,
										fileName: fileName,
										success: () => {
											console.log('分享成功');
										},
										fail: (err) => {
											console.error('分享失败:', err);
											uni.showToast({
												title: '分享失败',
												icon: 'none'
											});
										}
									});
								}
							}
						});
					},
					fail: (err) => {
						console.error('保存Excel文件失败:', err);
						uni.showToast({
							title: '保存失败: ' + (err.errMsg || '未知错误'),
							icon: 'none',
							duration: 3000
						});
					}
				});
				
			} catch (error) {
				console.error('导出Excel失败:', error);
				uni.showToast({
					title: '导出失败: ' + error.message,
					icon: 'none'
				});
			}
		}
	}
};
</script>

<style scoped>
.page {
	background: #f5f5f7;
	min-height: 100vh;
	padding: 12px;
	padding-bottom: 80px;
}

/* 搜索卡片 */
.search-card {
	background: white;
	border-radius: 16px;
	padding: 15px;
	box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
	margin-bottom: 12px;
}

.form-group {
	margin-bottom: 12px;
}

.form-label {
	font-size: 13px;
	font-weight: 600;
	color: #333;
	margin-bottom: 6px;
	display: block;
}

.input-wrapper {
	position: relative;
}

.form-input {
	width: 100%;
	height: 42px;
	padding: 0 35px 0 12px;
	border: 2px solid #e5e7eb;
	border-radius: 10px;
	font-size: 15px;
	background: #f9fafb;
	box-sizing: border-box;
	line-height: 38px;
}

.clear-btn {
	position: absolute;
	right: 10px;
	top: 50%;
	transform: translateY(-50%);
	width: 20px;
	height: 20px;
	background: #ccc;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
}

.clear-btn .icon {
	color: white;
	font-size: 12px;
}

.form-select {
	width: 100%;
	height: 42px;
	padding: 0 12px;
	border: 2px solid #e5e7eb;
	border-radius: 10px;
	font-size: 15px;
	background: #f9fafb;
	display: flex;
	justify-content: space-between;
	align-items: center;
	box-sizing: border-box;
}

.arrow {
	font-size: 12px;
	color: #999;
}

.autocomplete-list {
	position: absolute;
	top: 100%;
	left: 0;
	right: 0;
	background: white;
	border: 2px solid #e5e7eb;
	border-top: none;
	border-radius: 0 0 12px 12px;
	max-height: 200px;
	overflow-y: auto;
	z-index: 100;
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.autocomplete-item {
	padding: 10px 12px;
	border-bottom: 1px solid #f3f4f6;
	display: flex;
	justify-content: space-between;
}

.autocomplete-item:last-child {
	border-bottom: none;
}

.stock-code {
	color: #667eea;
	font-weight: 600;
	font-size: 14px;
}

.stock-name {
	color: #6b7280;
	font-size: 14px;
}

.btn-primary {
	width: 100%;
	height: 42px;
	padding: 0;
	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
	color: white;
	border: none;
	border-radius: 10px;
	font-size: 15px;
	font-weight: 600;
	box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
	line-height: 42px;
}

.btn-primary[disabled] {
	opacity: 0.5;
}

/* Tab切换 */
.tab-container {
	background: white;
	border-radius: 16px;
	padding: 10px;
	box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
	margin-bottom: 12px;
}

.tabs {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 6px;
}

.tab {
	flex: 1;
	padding: 8px 4px;
	border-radius: 8px;
	background: #f3f4f6;
	color: #6b7280;
	font-size: 13px;
	font-weight: 600;
	text-align: center;
}

.tab.active {
	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
	color: white;
	box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
}

/* 时间选择器 */
.time-selector {
	background: white;
	border-radius: 16px;
	padding: 12px;
	box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
	margin-bottom: 12px;
}

.time-label {
	font-size: 13px;
	font-weight: 600;
	color: #333;
	margin-bottom: 6px;
	display: block;
}

.time-select-btn {
	width: 100%;
	height: 42px;
	padding: 0 12px;
	background: #f9fafb;
	border: 2px solid #e5e7eb;
	border-radius: 10px;
	font-size: 15px;
	color: #333;
	display: flex;
	justify-content: space-between;
	align-items: center;
	box-sizing: border-box;
}

.time-text {
	flex: 1;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* K线图卡片 */
.chart-card {
	background: white;
	border-radius: 16px;
	padding: 12px;
	box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
	margin-bottom: 12px;
}

.chart-header {
	margin-bottom: 12px;
}

.stock-info {
	display: flex;
	justify-content: space-between;
	align-items: center;
}

.stock-title {
	font-size: 16px;
	font-weight: 700;
	color: #333;
}

.stock-price {
	font-size: 20px;
	font-weight: 700;
	color: #ef4444;
	display: block;
}

.stock-change {
	font-size: 13px;
	display: block;
	margin-top: 4px;
}

.stock-change.positive {
	color: #ef4444;
}

.stock-change.negative {
	color: #10b981;
}

.chart-container {
	height: 250px;
	position: relative;
}

.chart-canvas {
	width: 100%;
	height: 100%;
	z-index: 9;
}

/* K线详情弹窗 */
.kline-detail-popup {
	position: absolute;
	top: 5px;
	left: 50%;
	transform: translateX(-50%);
	width: 200px;
	background: rgba(0, 0, 0, 0.60);
	border-radius: 8px;
	padding: 8px;
	z-index: 10;
	box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
	animation: fadeIn 0.2s;
}

@keyframes fadeIn {
	from {
		opacity: 0;
		transform: translateY(-10px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

.detail-item {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 4px;
}

.detail-item:last-child {
	margin-bottom: 0;
}

.detail-label {
	font-size: 11px;
	color: #fff;
	opacity: 0.6;
}

.detail-value {
	font-size: 12px;
	font-weight: 500;
	color: #fff;
	margin-left: 8px;
}

.detail-value.positive {
	color: #ff6b6b;
}

.detail-value.negative {
	color: #51cf66;
}

/* 数据指标 */
.metrics-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 8px;
	margin-top: 12px;
}

.metric-item {
	background: #f9fafb;
	padding: 8px;
	border-radius: 10px;
}

.metric-label {
	font-size: 11px;
	color: #6b7280;
	margin-bottom: 3px;
	display: block;
}

.metric-value {
		font-size: 14px;
	font-weight: 600;
	color: #333;
	display: block;
}

.metric-value.positive {
	color: #ef4444;
}

.metric-value.negative {
	color: #10b981;
}

/* 时间选择弹窗 */
.picker-overlay {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(0, 0, 0, 0.4);
	z-index: 1000;
	display: flex;
	align-items: flex-end;
}

.picker-container {
	width: 100%;
	background: white;
	border-radius: 20px 20px 0 0;
	animation: slideUp 0.3s;
}

@keyframes slideUp {
	from {
		transform: translateY(100%);
	}
	to {
		transform: translateY(0);
	}
}

.picker-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 15px 20px;
	border-bottom: 1px solid #e5e7eb;
}

.picker-cancel,
.picker-confirm {
	background: none;
	border: none;
	font-size: 16px;
	padding: 5px 10px;
}

.picker-cancel {
	color: #6b7280;
}

.picker-confirm {
	color: #667eea;
	font-weight: 600;
}

.picker-title {
	font-size: 16px;
	font-weight: 600;
	color: #333;
}

.picker-content {
	height: 250px;
	padding: 10px 0;
}

.picker-item {
	padding: 12px 20px;
	font-size: 16px;
	color: #333;
	text-align: center;
}

.picker-item.selected {
	background: #667eea;
	color: white;
	font-weight: 600;
}

/* 四度空间（TPO）卡片 */
.tpo-card {
	background: white;
	border-radius: 16px;
	padding: 12px;
	box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
	margin-bottom: 12px;
}

.tpo-header {
	margin-bottom: 12px;
}

.tpo-title {
	font-size: 16px;
	font-weight: 700;
	color: #333;
	display: block;
	margin-bottom: 12px;
}

.btn-export {
	width: 100%;
	height: 42px;
	padding: 0;
	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
	color: white;
	border: none;
	border-radius: 10px;
	font-size: 15px;
	font-weight: 600;
	box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
	line-height: 42px;
}

.tpo-controls {
	display: flex;
	gap: 12px;
}

.tpo-control-item {
	flex: 1;
}

.tpo-control-label {
	font-size: 12px;
	color: #6b7280;
	display: block;
	margin-bottom: 6px;
}

.tpo-input {
	width: 100%;
	height: 36px;
	padding: 0 10px;
	border: 2px solid #e5e7eb;
	border-radius: 8px;
	font-size: 14px;
	background: #f9fafb;
	box-sizing: border-box;
}

.tpo-select {
	width: 100%;
	height: 36px;
	padding: 0 10px;
	border: 2px solid #e5e7eb;
	border-radius: 8px;
	font-size: 14px;
	background: #f9fafb;
	display: flex;
	justify-content: space-between;
	align-items: center;
	box-sizing: border-box;
}

.tpo-container {
	width: 100%;
	overflow-x: auto;
	overflow-y: hidden;
}

.tpo-canvas {
	width: 100%;
	display: block;
}

/* 底部TabBar */
.tab-bar {
	position: fixed;
	bottom: 0;
	left: 0;
	right: 0;
	background: white;
	display: flex;
	justify-content: space-around;
	padding: 8px 0 20px;
	border-top: 1px solid #e5e7eb;
	box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
	z-index: 999;
}

.tab-bar-item {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	color: #9ca3af;
}

.tab-bar-item.active {
	color: #667eea;
}

.tab-bar-icon {
	font-size: 22px;
}

.tab-bar-label {
	font-size: 10px;
	font-weight: 500;
	}
</style>
